import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import * as moment from 'moment';
import { Between, Like, Repository } from 'typeorm';
import { Area } from './entity/area.entity';
import { UserArea } from './entity/user-area.entity';
import { User } from './entity/user.entity';
import { WeatherInfo } from './entity/weather-info.entity';
import { RedisService } from './util/redis/redis.service';

@Injectable()
export class AppService {
  @InjectRepository(User)
  private userRepository: Repository<User>;
  @InjectRepository(UserArea)
  private userAreaRepository: Repository<UserArea>;
  @InjectRepository(WeatherInfo)
  private weatherInfoRepository: Repository<WeatherInfo>;
  @InjectRepository(Area)
  private areaRepository: Repository<Area>;
  @Inject()
  private redisService: RedisService

  /**
   * 用户登录，这里直接返回数据库的一条记录
   * @returns {Promise<User>} - 用户信息
   */
  userLogin(): Promise<User> {
    return this.userRepository.findOne();
  }

  /**
   * 查询天气列表，如果用户登录了就查询用户的收藏列表，否则就只查询用户当前所在地的天气信息。
   * @param {Object} info - 区域信息
   * @param {string} info.province - 省名称
   * @param {string} info.city - 市名称
   * @param {string} info.area - 区名称
   * @param {number} uid - 用户ID
   */
  async getWeatherInfoList(info: { area: string, city: string, province: string }, uid?: number) {
    const curArea = await this.areaRepository.findOne(info);
    if (!curArea) {
      throw new BadRequestException('不正确的地址信息');
    }
    const areaIds = new Set([curArea.id]);
    if (uid) {
      const userAreas = await this.userAreaRepository.find({ userId: uid });
      userAreas.forEach(({ areaId }) => areaIds.add(areaId));
    }
    return Promise.all(Array.from(areaIds).map(async areaId => {
      const area = await this.areaRepository.findOne(areaId);
      const infoList = await this.getWeatherFromCache(areaId);
      area.isCurrent = areaId === curArea.id;
      return { area, infoList };
    }));
  }

  /**
   * 根据区域ID获取该所在地的天气信息
   * @param areaId 
   */
  async getWeatherInfo(areaId: number) {
    const area = await this.areaRepository.findOne(areaId);
    if (!area) {
      throw new BadRequestException('invalid area');
    }
    const infoList = await this.getWeatherFromCache(areaId);
    return { area, infoList };
  }

  async getAreaListByName(areaName: string) {
    return this.areaRepository.find({ area: Like(`${areaName}%`) });
  }

  /**
   * 收藏接口
   * @param uid 用户ID
   * @param areaId 区域ID
   */
  async collectArea(uid: number, areaId: number) {
    const area = await this.areaRepository.findOne(areaId);
    if (!area) {
      throw new BadRequestException('invalid area');
    }
    const userArea = this.userAreaRepository.create();
    userArea.userId = uid;
    userArea.areaId = areaId;
    try {
      await this.userAreaRepository.save(userArea);
    } catch (err) {
      throw new BadRequestException('重复提交');
    }

  }

  /**
   * 从缓存中获取天气的信息，如果缓存里面没有，则从数据库获取并且更新缓存。
   * 缓存的有效时间这里设置为5分钟，实际上以天气更新频率来控制。
   * @param areaId 区域ID
   */
  private async getWeatherFromCache(areaId: number) {
    const cache = this.redisService.get('weather');
    const res = await cache.get(`area-${areaId}`);
    if (res) {
      return JSON.parse(res);
    }
    const area = await this.areaRepository.findOne(areaId);
    if (!area) {
      throw new BadRequestException('invalid area');
    }
    const weatherInfo = await this.weatherInfoRepository
      .find({
        areaId,
        date: Between(moment().format('YYYY-MM-DD'), moment().add(2, 'days').format('YYYY-MM-DD')),
      });
    await cache.setex(`area-${areaId}`, 60 * 5, JSON.stringify(weatherInfo));
    return weatherInfo;
  }
}


